import assert from "node:assert";
import { createRequire } from "node:module";
import path from "node:path";

import { visit } from "recast";

import pkgJson from "../package.json" with { type: "json" };

import type { BuildStep } from "./build.ts";
import { applyRecast } from "./helpers.ts";

const require = createRequire(import.meta.url);

const { version } = pkgJson;
assert.strictEqual(
  typeof version,
  "string",
  '"version" field missing from package.json'
);

export const updateVersion: BuildStep = async (options) => {
  await applyRecast({
    glob: `version.${options.jsExt}`,
    cwd: options.targetDir,
    transformStep({ ast }) {
      return {
        ast: visit(ast, {
          visitStringLiteral(path) {
            const node = path.node;
            if (node.value === "local") {
              node.value = version;
            }
            if (node.value === "source") {
              node.value = options.type;
            }
            this.traverse(path);
          },
        }),
      };
    },
  });
};

export const verifyVersion: BuildStep = async (options) => {
  const { ApolloClient, InMemoryCache } = require(
    path.join(
      options.rootDir,
      options.targetDir,
      "core",
      `index.${options.jsExt}`
    )
  );

  // Though this may seem like overkill, verifying that ApolloClient is
  // constructible in Node.js is actually pretty useful, too!
  const client = new ApolloClient({
    cache: new InMemoryCache(),
  });

  // Probably not necessary, but it seems wise to clean up any resources
  // the client might have acquired during its construction.
  client.stop();

  // The CommonJS dist/core/core.cjs file is generated from ESM modules
  // generated by tsc, including dist/version.js, so verifying core.cjs
  // exports an ApolloClient class that defines client.version also serves to
  // verify that dist/version.js must have been correctly updated, which is
  // convenient because dist/version.js uses ECMAScript module syntax, and is
  // thus not importable in all versions of Node.js.
  assert.strictEqual(
    client.version,
    version,
    "Failed to update dist/version.js and dist/core/core.cjs"
  );
};
